{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from sklearn.feature_extraction import DictVectorizer\n",
    "from sklearn.model_selection import train_test_split, GridSearchCV\n",
    "from sklearn.tree import DecisionTreeClassifier, export_graphviz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 1313 entries, 0 to 1312\n",
      "Data columns (total 11 columns):\n",
      " #   Column     Non-Null Count  Dtype  \n",
      "---  ------     --------------  -----  \n",
      " 0   row.names  1313 non-null   int64  \n",
      " 1   pclass     1313 non-null   object \n",
      " 2   survived   1313 non-null   int64  \n",
      " 3   name       1313 non-null   object \n",
      " 4   age        633 non-null    float64\n",
      " 5   embarked   821 non-null    object \n",
      " 6   home.dest  754 non-null    object \n",
      " 7   room       77 non-null     object \n",
      " 8   ticket     69 non-null     object \n",
      " 9   boat       347 non-null    object \n",
      " 10  sex        1313 non-null   object \n",
      "dtypes: float64(1), int64(2), object(8)\n",
      "memory usage: 113.0+ KB\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "决策树对泰坦尼克号进行预测生死\n",
    ":return: None\n",
    "\"\"\"\n",
    "titan = pd.read_csv('./data/titanic.txt')\n",
    "titan.info()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 1313 entries, 0 to 1312\n",
      "Data columns (total 3 columns):\n",
      " #   Column  Non-Null Count  Dtype  \n",
      "---  ------  --------------  -----  \n",
      " 0   pclass  1313 non-null   object \n",
      " 1   age     633 non-null    float64\n",
      " 2   sex     1313 non-null   object \n",
      "dtypes: float64(1), object(2)\n",
      "memory usage: 30.9+ KB\n"
     ]
    }
   ],
   "source": [
    "# 处理数据，找出特征值和目标值\n",
    "x = titan[['pclass', 'age', 'sex']]\n",
    "y = titan['survived']\n",
    "x.info()  # 用来判断是否有空值"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\ThinkPad\\AppData\\Local\\Temp\\ipykernel_7100\\1952858098.py:2: SettingWithCopyWarning: \n",
      "A value is trying to be set on a copy of a slice from a DataFrame\n",
      "\n",
      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
      "  x['age'].fillna(x['age'].mean(), inplace=True)\n"
     ]
    }
   ],
   "source": [
    "# 进行缺失值处理\n",
    "x['age'].fillna(x['age'].mean(), inplace=True)\n",
    "# 分割数据集到训练集合测试集\n",
    "x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25, random_state=4)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['age', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', 'sex=female', 'sex=male']\n",
      "  (0, 0)\t30.0\n",
      "  (0, 2)\t1.0\n",
      "  (0, 5)\t1.0\n",
      "  (1, 0)\t62.0\n",
      "  (1, 1)\t1.0\n",
      "  (1, 5)\t1.0\n",
      "  (2, 0)\t31.19418104265403\n",
      "  (2, 3)\t1.0\n",
      "  (2, 4)\t1.0\n",
      "  (3, 0)\t31.19418104265403\n",
      "  (3, 1)\t1.0\n",
      "  (3, 4)\t1.0\n",
      "  (4, 0)\t64.0\n",
      "  (4, 2)\t1.0\n",
      "  (4, 5)\t1.0\n",
      "  (5, 0)\t31.19418104265403\n",
      "  (5, 1)\t1.0\n",
      "  (5, 4)\t1.0\n",
      "  (6, 0)\t24.0\n",
      "  (6, 3)\t1.0\n",
      "  (6, 4)\t1.0\n",
      "  (7, 0)\t31.19418104265403\n",
      "  (7, 3)\t1.0\n",
      "  (7, 5)\t1.0\n",
      "  (8, 0)\t31.19418104265403\n",
      "  :\t:\n",
      "  (975, 4)\t1.0\n",
      "  (976, 0)\t18.0\n",
      "  (976, 2)\t1.0\n",
      "  (976, 4)\t1.0\n",
      "  (977, 0)\t31.19418104265403\n",
      "  (977, 3)\t1.0\n",
      "  (977, 4)\t1.0\n",
      "  (978, 0)\t31.19418104265403\n",
      "  (978, 2)\t1.0\n",
      "  (978, 5)\t1.0\n",
      "  (979, 0)\t31.19418104265403\n",
      "  (979, 2)\t1.0\n",
      "  (979, 5)\t1.0\n",
      "  (980, 0)\t28.0\n",
      "  (980, 3)\t1.0\n",
      "  (980, 5)\t1.0\n",
      "  (981, 0)\t34.0\n",
      "  (981, 2)\t1.0\n",
      "  (981, 5)\t1.0\n",
      "  (982, 0)\t46.0\n",
      "  (982, 1)\t1.0\n",
      "  (982, 5)\t1.0\n",
      "  (983, 0)\t31.19418104265403\n",
      "  (983, 3)\t1.0\n",
      "  (983, 5)\t1.0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Program Files\\Python39\\lib\\site-packages\\sklearn\\utils\\deprecation.py:87: FutureWarning: Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n",
      "  warnings.warn(msg, category=FutureWarning)\n"
     ]
    }
   ],
   "source": [
    "# 进行处理（特征工程）特征-》类别-》one_hot编码(因为处理的数据中有非数值)\n",
    "dict = DictVectorizer()\n",
    "# 这一步是对字典进行特征抽取,to_dict可以把df变为字典，records代表列名变为键\n",
    "x_train = dict.fit_transform(x_train.to_dict(orient=\"records\"))\n",
    "print(dict.get_feature_names())\n",
    "x_test = dict.transform(x_test.to_dict(orient=\"records\"))\n",
    "print(x_train)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_score： 0.8648373983739838\n",
      "test_score： 0.8085106382978723\n"
     ]
    }
   ],
   "source": [
    "# 用决策树进行预测，修改max_depth试试(默认基尼系数)\n",
    "dec = DecisionTreeClassifier()\n",
    "#训练\n",
    "dec.fit(x_train, y_train)\n",
    "# 预测准确率\n",
    "print(\"train_score：\", dec.score(x_train, y_train))\n",
    "print(\"test_score：\", dec.score(x_test, y_test))"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调整后的参数： {'criterion': 'gini', 'max_depth': 10, 'min_samples_split': 6}\n",
      "测试分数： 0.8191300762729334\n",
      "选择最好的模型是： DecisionTreeClassifier(max_depth=10, min_samples_split=6)\n"
     ]
    }
   ],
   "source": [
    "# 调参\n",
    "# min_samples_split、max_depth 预剪枝\n",
    "# min_sample_split是分割所需的最小样本数。例如，如果min_sample_split = 6并且节点中有4个样本，则不会发生拆分（不管熵是多少）\n",
    "# min_sample_leaf基本上是叶节点所需的最小样本数。假设min_sample_leaf = 3并且一个含有5个样本的节点可以分别分裂成2个和3个大小的叶子节点，\n",
    "# 那么这个分裂就不会发生，因为最小的叶子大小为3\n",
    "\n",
    "param = [{'criterion':['gini','entropy'],\n",
    "         'max_depth':np.arange(10,50,10),\n",
    "         'min_samples_split':np.arange(2,8)}]\n",
    "dec = GridSearchCV(DecisionTreeClassifier(),param_grid=param,cv=10)\n",
    "dec.fit(x_train,y_train)\n",
    "print('调整后的参数：',dec.best_params_)\n",
    "print('测试分数：',dec.best_score_)\n",
    "print(\"选择最好的模型是：\", dec.best_estimator_)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_score： 0.8577235772357723\n",
      "test_score： 0.817629179331307\n"
     ]
    }
   ],
   "source": [
    "# 按最优参数生成决策树\n",
    "model = DecisionTreeClassifier(max_depth=10,min_samples_split=6)\n",
    "model.fit(x_train,y_train)\n",
    "print(\"train_score：\", model.score(x_train, y_train))\n",
    "print(\"test_score：\", model.score(x_test, y_test))\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "outputs": [],
   "source": [
    "# 导出决策树的结构\n",
    "export_graphviz(model, out_file=\"tree.dot\",\n",
    "                feature_names=['年龄', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', '女性', '男性'])\n",
    "#终端输入 dot -Tpng tree.dot -o tree.png"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}